/*******************************************************************************
 * Copyright (c) 2014, 2025 Actuate Corporation and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * https://www.eclipse.org/legal/epl-2.0/.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.engine.emitter.pptx;

import java.awt.Color;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Iterator;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.birt.report.engine.content.IHyperlinkAction;
import org.eclipse.birt.report.engine.emitter.EmitterUtil;
import org.eclipse.birt.report.engine.emitter.ppt.util.PPTUtil.HyperlinkDef;
import org.eclipse.birt.report.engine.emitter.pptx.writer.Presentation;
import org.eclipse.birt.report.engine.layout.emitter.Image;
import org.eclipse.birt.report.engine.layout.emitter.util.BackgroundImageLayout;
import org.eclipse.birt.report.engine.layout.emitter.util.Position;
import org.eclipse.birt.report.engine.layout.pdf.font.FontInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.BackgroundImageInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo;
import org.eclipse.birt.report.engine.nLayout.area.style.TextStyle;
import org.eclipse.birt.report.engine.ooxml.IPart;
import org.eclipse.birt.report.engine.ooxml.ImageManager;
import org.eclipse.birt.report.engine.ooxml.ImageManager.ImagePart;
import org.eclipse.birt.report.engine.ooxml.util.OOXmlUtil;
import org.eclipse.birt.report.engine.ooxml.writer.OOXmlWriter;

import org.openpdf.text.Font;

/**
 * This class is used use to generate PPTX shapes.
 *
 */
public class PPTXCanvas {

	private static Logger logger = Logger.getLogger(PPTXCanvas.class.getName());

	private final Presentation presentation;
	private final IPart part;
	private final ImageManager imageManager;
	private final OOXmlWriter writer;
	private float scale = 1;

	public PPTXCanvas(Presentation presentation, IPart part, OOXmlWriter writer) {
		this.presentation = presentation;
		this.part = part;
		this.imageManager = (ImageManager) part.getPackage().getExtensionData();
		this.writer = writer;
	}

	public PPTXCanvas(PPTXCanvas canvas, OOXmlWriter writer) {
		this.presentation = canvas.presentation;
		this.part = canvas.part;
		this.imageManager = canvas.imageManager;
		this.writer = writer;
		this.clipStack = canvas.clipStack;
	}

	/**
	 * @param startX
	 * @param startY
	 * @param endX
	 * @param endY
	 * @param width
	 * @param color
	 * @param lineStyle
	 *
	 *                  pre: all are set in EMU units
	 */
	public void drawLine(int startX, int startY, int endX, int endY, int width, Color color, int lineStyle) {
		if (color == null || width == 0f || lineStyle == BorderInfo.BORDER_STYLE_NONE) {
			return;
		}
		writer.openTag("p:cxnSp");
		writer.openTag("p:nvCxnSpPr");
		writer.openTag("p:cNvPr");
		int shapeId = nextShapeId();
		writer.attribute("id", shapeId);
		writer.attribute("name", "Line " + shapeId);
		writer.closeTag("p:cNvPr");
		writer.openTag("p:cNvCxnSpPr");
		writer.closeTag("p:cNvCxnSpPr");
		writer.openTag("p:nvPr");
		writer.closeTag("p:nvPr");
		writer.closeTag("p:nvCxnSpPr");
		writer.openTag("p:spPr");
		setPosition(startX, startY, endX - startX, endY - startY);
		writer.openTag("a:prstGeom");
		writer.attribute("prst", "line");
		writer.closeTag("a:prstGeom");
		setProperty(color, width, lineStyle);
		writer.closeTag("p:spPr");
		writer.closeTag("p:cxnSp");
	}

	public void drawText(String text, int textX, int textY, int width, int height, String fontName, float fontSize,
			int fontStyle, Color color, boolean isUnderline, boolean isLineThrough, HyperlinkDef link) {
		writer.openTag("p:sp");
		writer.openTag("p:nvSpPr");
		writer.openTag("p:cNvPr");
		int shapeId = nextShapeId();
		writer.attribute("id", shapeId);
		writer.attribute("name", "TextBox " + shapeId);
		writer.closeTag("p:cNvPr");
		writer.openTag("p:cNvSpPr");
		writer.attribute("txBox", "1");
		writer.closeTag("p:cNvSpPr");
		writer.openTag("p:nvPr");
		writer.closeTag("p:nvPr");
		writer.closeTag("p:nvSpPr");
		writer.openTag("p:spPr");
		setPosition(textX, textY, width + 1, height);
		writer.openTag("a:prstGeom");
		writer.attribute("prst", "rect");
		writer.closeTag("a:prstGeom");
		writer.closeTag("p:spPr");
		writer.openTag("p:txBody");
		writer.openTag("a:bodyPr");
		writer.attribute("wrap", "none");
		writer.attribute("lIns", "0");
		writer.attribute("tIns", "0");
		writer.attribute("rIns", "0");
		writer.attribute("bIns", "0");
		writer.attribute("rtlCol", "0");
		writer.closeTag("a:bodyPr");
		writer.openTag("a:p");
		writer.openTag("a:r");
		setTextProperty(fontName, fontSize, fontStyle, color, isUnderline, isLineThrough, link);
		writer.openTag("a:t");
		writeText(text);
		writer.closeTag("a:t");
		writer.closeTag("a:r");
		writer.closeTag("a:p");
		writer.closeTag("p:txBody");
		writer.closeTag("p:sp");
	}

	/**
	 * Word have extra limitation on text in run: a. it must following xml format.
	 * b. no ]]> so , we need replace all &, <,> in the text
	 *
	 * @param text
	 */
	void writeText(String text) {
		int length = text.length();
		StringBuilder sb = new StringBuilder(length * 2);
		for (int i = 0; i < length; i++) {
			char ch = text.charAt(i);
			switch (ch) {
			case '&':
				sb.append("&amp;");
				break;
			case '>':
				sb.append("&gt;");
				break;
			case '<':
				sb.append("&lt;");
				break;
			default:
				sb.append(ch);
			}
		}
		writer.cdata(sb.toString());
	}

	public void drawImage(String uri, String extension, int imageX, int imageY, int height, int width, String helpText,
			HyperlinkDef link) throws IOException {
		byte[] imageData = EmitterUtil.getImageData(uri);
		IPart imagePart = imageManager.getImagePart(part, uri, imageData).getPart();
		drawImage(imagePart, imageX, imageY, height, width, helpText, true, link);
	}

	public void drawImage(String imageId, byte[] imageData, String extension, int imageX, int imageY, int height,
			int width, String helpText, HyperlinkDef link) throws IOException {
		drawImage(imageId, imageData, extension, imageX, imageY, height, width, helpText, true, link);
	}

	private void drawImage(String imageId, byte[] imageData, String extension, int imageX, int imageY, int height,
			int width, String helpText, boolean stretch, HyperlinkDef link) throws IOException {
		IPart imagePart = imageManager.getImagePart(part, imageId, imageData).getPart();
		drawImage(imagePart, imageX, imageY, height, width, helpText, stretch, link);
	}

	private Crop checkCrop(int x, int y, int width, int height) {
		if (clipStack.isEmpty()) {
			return null;
		}
		ClipArea clip = clipStack.peek();
		int left = 0, right = 0, top = 0, bottom = 0;
		if (x < clip.x) {
			left = (int) ((clip.x - x) / (float) width * 100000);
		}
		if (y < clip.y) {
			top = (int) ((clip.y - y) / (float) height * 100000);
		}
		if (x + width > clip.x + clip.width) {
			right = (int) (((x + width) - (clip.x + clip.width)) / (float) width * 100000);
		}
		if (y + height > clip.y + clip.height) {
			bottom = (int) (((y + height) - (clip.y + clip.height)) / (float) height * 100000);
		}
		if (left != 0 || right != 0 || top != 0 || bottom != 0) {
			return new Crop(left, right, top, bottom);
		}
		return null;
	}

	private class Crop {

		int left, right, top, bottom;

		Crop(int left, int right, int top, int bottom) {
			this.left = left;
			this.right = right;
			this.top = top;
			this.bottom = bottom;
		}
	}

	private void drawImage(IPart imagePart, int imageX, int imageY, int height, int width, String helpText,
			boolean stretch, HyperlinkDef link) {
		String relationshipId = imagePart.getRelationshipId();
		writer.openTag("p:pic");
		writer.openTag("p:nvPicPr");
		writer.openTag("p:cNvPr");
		int shapeId = nextShapeId();
		writer.attribute("id", shapeId);
		writer.attribute("name", "Image " + shapeId);
		writer.attribute("descr", helpText);
		// handle hyperlink and bookmark of images
		if (link != null) {
			if (link.getHyperlinkActionType() != IHyperlinkAction.ACTION_BOOKMARK)
				setHyperlink(link);
			else {
				String bmk = link.getLink();
				if (bmk != null) {
					String bmk_relationshipid = this.getPresentation().getBookmarkRelationshipid(bmk);
					setBookmark(bmk_relationshipid);
				}
			}
		}
		writer.closeTag("p:cNvPr");
		writer.openTag("p:cNvPicPr");
		writer.openTag("a:picLocks");
		writer.attribute("noChangeAspect", "1");
		writer.closeTag("a:picLocks");
		writer.closeTag("p:cNvPicPr");
		writer.openTag("p:nvPr");
		writer.closeTag("p:nvPr");
		writer.closeTag("p:nvPicPr");
		writer.openTag("p:blipFill");
		Crop crop = checkCrop(imageX, imageY, width, height);
		if (crop != null) {
			writer.attribute("rotWithShape", "1");
		}
		writer.openTag("a:blip");
		writer.attribute("r:embed", relationshipId);
		writer.closeTag("a:blip");
		if (crop != null) {
			writer.openTag("a:srcRect");
			if (crop.top != 0) {
				writer.attribute("t", crop.top);
			}
			if (crop.left != 0) {
				writer.attribute("l", crop.left);
			}
			if (crop.right != 0) {
				writer.attribute("r", crop.right);
			}
			if (crop.bottom != 0) {
				writer.attribute("b", crop.bottom);
			}
			writer.closeTag("a:srcRect");
		}
		if (stretch) {
			writer.openTag("a:stretch");
			// writer.openTag("a:fillRect");
			// writer.closeTag("a:fillRect");
			writer.closeTag("a:stretch");
		}
		writer.closeTag("p:blipFill");
		writer.openTag("p:spPr");
		if (crop == null) {
			setPosition(imageX, imageY, width, height);
		} else {
			ClipArea clip = clipStack.peek();
			int pX = Math.max(clip.x, imageX);
			int pY = Math.max(clip.y, imageY);
			int pWidth = Math.min(imageX + width, clip.x + clip.width) - pX;
			int pHeight = Math.min(imageY + height, clip.y + clip.height) - pY;
			pHeight = pHeight < 0 ? 0 : pHeight;
			pWidth = pWidth < 0 ? 0 : pWidth;
			setPosition(pX, pY, pWidth, pHeight);
		}
		writer.openTag("a:prstGeom");
		writer.attribute("prst", "rect");
		writer.closeTag("a:prstGeom");
		writer.closeTag("p:spPr");
		writer.closeTag("p:pic");
	}

	public void drawBackgroundColor(Color color, int x, int y, int width, int height) {
		if (color != null) {
			writer.openTag("p:sp");
			writer.openTag("p:nvSpPr");
			writer.openTag("p:cNvPr");
			int shapeId = nextShapeId();
			writer.attribute("id", shapeId);
			writer.attribute("name", "Rectangle " + shapeId);
			writer.closeTag("p:cNvPr");
			writer.openTag("p:cNvSpPr");
			writer.closeTag("p:cNvSpPr");
			writer.openTag("p:nvPr");
			writer.closeTag("p:nvPr");
			writer.closeTag("p:nvSpPr");
			writer.openTag("p:spPr");
			setPosition(x, y, width, height);
			writer.openTag("a:prstGeom");
			writer.attribute("prst", "rect");
			writer.closeTag("a:prstGeom");
			setBackgroundColor(color);
			writer.closeTag("p:spPr");
			writer.closeTag("p:sp");
		}
	}

	public void drawBackgroundImage(int x, int y, int width, int height, int imageWidth, int imageHeight, int repeat,
			String imageURI, byte[] imageData, int offsetX, int offsetY) {
		if (imageURI == null || imageURI.length() == 0 || imageData == null || imageData.length == 0) {
			return;
		}
		try {
			if (!imageManager.hasImage(imageURI)) {
				org.eclipse.birt.report.engine.layout.emitter.Image image = EmitterUtil.parseImage(imageData, null,
						null);
				imageData = image.getData();
			}

			ImagePart imagePartInfo = imageManager.getImagePart(part, imageURI, imageData);
			Image imageInfo = imagePartInfo.getImageInfo();

			float originalImageWidth = imageWidth != 0 ? imageWidth : imageInfo.getWidth();
			float originalImageHeight = imageHeight != 0 ? imageHeight : imageInfo.getHeight();
			originalImageHeight = Math.min(originalImageHeight, height);
			originalImageWidth = Math.min(originalImageWidth, width);
			Position areaPosition = new Position(x, y);
			Position areaSize = new Position(width, height);
			Position imagePosition = new Position(x + offsetX, y + offsetY);
			Position imageSize = new Position(originalImageWidth, originalImageHeight);
			BackgroundImageLayout layout = new BackgroundImageLayout(areaPosition, areaSize, imagePosition, imageSize);
			Collection<?> positions = layout.getImagePositions(repeat);
			Iterator<?> iterator = positions.iterator();
			while (iterator.hasNext()) {
				Position position = (Position) iterator.next();
				fillRectangleWithImage(imagePartInfo, (int) OOXmlUtil.convertPointerToEmus(position.getX()),
						(int) OOXmlUtil.convertPointerToEmus(position.getY()),
						(int) OOXmlUtil.convertPointerToEmus(originalImageWidth),
						(int) OOXmlUtil.convertPointerToEmus(originalImageHeight), 0, 0);
			}
		} catch (IOException e) {
			logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}
	}

	private void fillRectangleWithImage(ImagePart imageInfo, int x, int y, int width, int height, int offsetX,
			int offsetY) {
		writer.openTag("p:sp");
		writer.openTag("p:nvSpPr");
		writer.openTag("p:cNvPr");
		int shapeId = nextShapeId();
		writer.attribute("id", shapeId);
		writer.attribute("name", "Rectangle " + shapeId);
		writer.closeTag("p:cNvPr");
		writer.openTag("p:cNvSpPr");
		writer.closeTag("p:cNvSpPr");
		writer.openTag("p:nvPr");
		writer.closeTag("p:nvPr");
		writer.closeTag("p:nvSpPr");
		writer.openTag("p:spPr");
		setPosition(x, y, width, height);
		writer.openTag("a:prstGeom");
		writer.attribute("prst", "rect");
		writer.closeTag("a:prstGeom");

		setBackgroundImg(imageInfo.getPart().getRelationshipId(), offsetX, offsetY, BackgroundImageInfo.NO_REPEAT);
		// hardcore the repeat type
		writer.openTag("a:ln");
		writer.openTag("a:noFill");
		writer.closeTag("a:noFill");
		writer.closeTag("a:ln");
		writer.closeTag("p:spPr");
		writer.closeTag("p:sp");
	}

	public void setBackgroundImg(String relationshipid, int offsetX, int offsetY) {
		setBackgroundImg(relationshipid, offsetX, offsetY, BackgroundImageInfo.REPEAT);
	}

	public void setBackgroundImg(String relationshipid, int offsetX, int offsetY, int repeatmode) {
		if (relationshipid == null || repeatmode < BackgroundImageInfo.NO_REPEAT
				|| repeatmode > BackgroundImageInfo.REPEAT || repeatmode == BackgroundImageInfo.REPEAT_X
				|| repeatmode == BackgroundImageInfo.REPEAT_Y) {// cases that are not supported: log on exception
			return;
		}
		writer.openTag("a:blipFill");
		writer.attribute("dpi", "0");
		writer.attribute("rotWithShape", "1");
		writer.openTag("a:blip");
		writer.attribute("r:embed", relationshipid);
		writer.closeTag("a:blip");

		switch (repeatmode) {
		case BackgroundImageInfo.REPEAT:
			writer.openTag("a:tile");
			writer.attribute("tx", offsetX);
			writer.attribute("ty", offsetY);
			writer.closeTag("a:tile");
			break;

		case BackgroundImageInfo.NO_REPEAT:
			writer.openTag("a:stretch");
			writer.openTag("a:fillRect");
			writer.attribute("b", offsetY);
			writer.attribute("r", offsetX);
			writer.closeTag("a:fillRect");
			writer.closeTag("a:stretch");
			break;
		}
		writer.closeTag("a:blipFill");
	}

	private void setTextProperty(String fontName, float fontSize, int fontStyle, Color color, boolean isUnderline,
			boolean isLineThrough, HyperlinkDef link) {
		writer.openTag("a:rPr");
		writer.attribute("lang", "en-US");
		writer.attribute("altLang", "zh-CN");
		writer.attribute("dirty", "0");
		writer.attribute("smtClean", "0");
		if (isLineThrough) {
			writer.attribute("strike", "sngStrike");
		}
		if (isUnderline) {
			writer.attribute("u", "sng");
		}
		writer.attribute("sz", (int) (fontSize * 100));
		boolean isItalic = (fontStyle & Font.ITALIC) != 0;
		boolean isBold = (fontStyle & Font.BOLD) != 0;
		if (isItalic) {
			writer.attribute("i", 1);
		}
		if (isBold) {
			writer.attribute("b", 1);
		}
		setBackgroundColor(color);
		setTextFont(fontName);
		setHyperlink(link);
		writer.closeTag("a:rPr");
	}

	void setHyperlink(HyperlinkDef link) {// TODO: set links for bookmark
		// power point doesn't support undecorated hyperlink
		if (link != null && link.isHasHyperlinkDecoration()) {
			String hyperlink = null;
			try {
				hyperlink = URLEncoder.encode(link.getLink(), "UTF-8");
			} catch (UnsupportedEncodingException ue) {
				logger.log(Level.SEVERE, ue.getLocalizedMessage(), ue);
			}
			if (hyperlink != null) {
				if (hyperlink.startsWith("\"") && hyperlink.endsWith("\"")) {
					hyperlink = hyperlink.substring(1, hyperlink.length() - 1);
				}
				writer.openTag("a:hlinkClick");
				writer.attribute("r:id", part.getHyperlinkId(hyperlink));
				if (link.getTooltip() != null) {
					writer.attribute("tooltip", link.getTooltip());
				}
				writer.closeTag("a:hlinkClick");
			}
		}
	}

	public void setBookmark(String bmk_relationshipid) {
		if (bmk_relationshipid != null) {
			writer.openTag("a:hlinkClick");
			writer.attribute("r:id", bmk_relationshipid);
			writer.attribute("action", "ppaction://hlinksldjump");
			writer.closeTag("a:hlinkClick");
		}

	}

	private void setTextFont(String fontName) {
		writer.openTag("a:latin");
		writer.attribute("typeface", fontName);
		writer.attribute("pitchFamily", "18");
		writer.attribute("charset", "0");
		writer.closeTag("a:latin");
		writer.openTag("a:cs");
		writer.attribute("typeface", fontName);
		writer.attribute("pitchFamily", "18");
		writer.attribute("charset", "0");
		writer.closeTag("a:cs");
	}

	public void setBackgroundColor(Color color) {
		if (color != null) {
			writer.openTag("a:solidFill");
			writer.openTag("a:srgbClr");
			writer.attribute("val", EmitterUtil.getColorString(color));
			writer.closeTag("a:srgbClr");
			writer.closeTag("a:solidFill");
		}
	}

	public void setPosition(int startX, int startY, int width, int height) {
		setPosition('a', startX, startY, width, height);
	}

	public void setPosition(char tagtype, int startX, int startY, int width, int height) {
		writer.openTag(tagtype + ":xfrm");
		writer.openTag("a:off");
		writer.attribute("x", startX);
		writer.attribute("y", startY);
		writer.closeTag("a:off");
		writer.openTag("a:ext");
		writer.attribute("cx", width);
		writer.attribute("cy", height);
		writer.closeTag("a:ext");
		writer.closeTag(tagtype + ":xfrm");
	}

	public void setProperty(Color color, int width, int style) {
		// module for outline line style
		writer.openTag("a:ln");
		writer.attribute("w", width);
		if (style == org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo.BORDER_STYLE_DOUBLE) {
			writer.attribute("cmpd", "dbl");
		}
		setBackgroundColor(color);

		// the other line styles, e.g. 'ridge', 'outset', 'groove', 'insert'
		// is NOT supported now and all regarded with default style, i.e, solid.
		switch (style) {
		case org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo.BORDER_STYLE_DOUBLE:
			setStyle("solid");
			break;
		case org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo.BORDER_STYLE_DASHED:
			setStyle("dash");
			break;
		case org.eclipse.birt.report.engine.nLayout.area.style.BorderInfo.BORDER_STYLE_DOTTED:
			setStyle("sysDash");
			break;
		default:
			setStyle("solid");
			break;
		}
		writer.closeTag("a:ln");
	}

	private void setStyle(String lineStyle) {
		writer.openTag("a:prstDash");
		writer.attribute("val", lineStyle);
		writer.closeTag("a:prstDash");
	}

	public Presentation getPresentation() {
		return presentation;
	}

	private int nextShapeId() // change to public
	{
		return presentation.getNextShapeId();
	}

	public void drawText(String text, int textX, int textY, int width, int height, TextStyle textStyle,
			HyperlinkDef link) {
		FontInfo fontInfo = textStyle.getFontInfo();
		String fontName = fontInfo.getFontName();
		float fontSize = fontInfo.getFontSize();
		int fontStyle = fontInfo.getFontStyle();
		Color color = textStyle.getColor();
		drawText(text, textX, textY, width, height, fontName, fontSize, fontStyle, color, textStyle.isUnderline(),
				textStyle.isLinethrough(), link);
	}

	private Stack<ClipArea> clipStack = new Stack<>();

	private class ClipArea {

		int x, y, width, height;

		ClipArea(int x, int y, int width, int height) {
			this.x = x;
			this.y = y;
			this.width = width;
			this.height = height;
		}
	}

	public void startClip(int startX, int startY, int width, int height) {
		if (clipStack.isEmpty()) {
			clipStack.push(new ClipArea(startX, startY, width, height));
		} else {
			ClipArea parent = clipStack.peek();
			int newX = Math.max(parent.x, startX);
			int newY = Math.max(parent.y, startY);
			int newWidth = Math.min(startX + width, parent.x + parent.width) - newX;
			int newHeight = Math.min(startY + height, parent.y + parent.height) - newY;
			clipStack.push(new ClipArea(newX, newY, newWidth, newHeight));
		}
	}

	public void endClip() {
		clipStack.pop();
	}

	public OOXmlWriter getWriter() {
		return writer;
	}

	protected void writeMarginProperties(int top, int right, int bottom, int left) {
		writer.attribute("marL", left);
		writer.attribute("marR", right);
		writer.attribute("marT", top);
		writer.attribute("marB", bottom);
	}

	public String getImageRelationship(BackgroundImageInfo bgimginfo) {
		if (bgimginfo == null) {
			return null;
		}
		String imageURI = bgimginfo.getUrl();
		byte[] imageData = bgimginfo.getImageData();
		String relationshipid = null;
		try {
			if (!imageManager.hasImage(imageURI)) {
				org.eclipse.birt.report.engine.layout.emitter.Image image = EmitterUtil.parseImage(imageData, null,
						null);
				// image does not exist
				if (image == null) {
					return null;
				}
				imageData = image.getData();
			}

			ImagePart imagePartInfo = imageManager.getImagePart(part, imageURI, imageData);
			relationshipid = imagePartInfo.getPart().getRelationshipId();
		}

		catch (IOException e) {
			logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}

		return relationshipid;
	}

	protected int getScaledValue(float value) {
		return (int) (value * scale);
	}

	public void setScale(float newscale) {
		scale = newscale;
	}
}
